package cloud.tianai.mate.captcha.validator.common.util;

import cloud.tianai.captcha.validator.common.model.dto.ImageCaptchaTrack;

import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

public class CaptchaDecryptUtils {
    private static final String BASE64_DICT = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ-~";
    private static final double[] POW_64 = new double[13]; // 用于存储64的幂次方，最多需要处理13位的64进制数

    static {
        for (int i = 0; i < POW_64.length; i++) {
            POW_64[i] = Math.pow(64, i);
        }
    }

    public static ImageCaptchaTrack decrypt(String encryptData) {
        byte[] bytes = encryptData.getBytes(StandardCharsets.UTF_8);
        long startTime = byte64to10(bytes, 0, 7);
        long stopTime = byte64to10(bytes, 7, 14);
        int bgImageWidth = (int) byte64to10(bytes, 14, 17);
        int bgImageHeight = (int) byte64to10(bytes, 17, 20);
        int templateImageWidth = (int) byte64to10(bytes, 20, 23);
        int templateImageHeight = (int) byte64to10(bytes, 23, 26);
        int left = (int) byte64to10(bytes, 26, 29);
        int top = (int) byte64to10(bytes, 29, 32);
        int trackCount = (int) byte64to10(bytes, 32, 35);
        int trackOffsetEnd = trackCount * 13 + 35;
        List<ImageCaptchaTrack.Track> trackList = new ArrayList<>();
        ImageCaptchaTrack.Track pre = null;
        for (int i = 0; i < trackCount; i++) {
            int blockStart = i * 13;
            ImageCaptchaTrack.Track track = decodeTrack2(pre, bytes, blockStart + 35);
            trackList.add(track);
            pre = track;
        }
        String data = null;
        if (trackOffsetEnd < encryptData.length()) {
            String encData = new String(bytes, trackOffsetEnd, bytes.length - trackOffsetEnd);
            data = decodeUnicode(encData);
        }
        ImageCaptchaTrack imageCaptchaTrack = new ImageCaptchaTrack();
        imageCaptchaTrack.setStartTime(new Date(startTime * 1000L));
        imageCaptchaTrack.setStopTime(new Date(stopTime * 1000L));
        imageCaptchaTrack.setBgImageWidth(bgImageWidth);
        imageCaptchaTrack.setBgImageHeight(bgImageHeight);
        imageCaptchaTrack.setTemplateImageWidth(templateImageWidth);
        imageCaptchaTrack.setTemplateImageHeight(templateImageHeight);
        imageCaptchaTrack.setTrackList(trackList);
        imageCaptchaTrack.setLeft(left);
        imageCaptchaTrack.setTop(top);
        imageCaptchaTrack.setData(data);

        return imageCaptchaTrack;
    }

    private static String decodeUnicode(String encodedStr) {
        String[] parts = encodedStr.split(",");
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < parts.length; i++) {
            sb.append((char) Integer.parseInt(parts[i]));
        }
        return sb.toString();
    }

    private static ImageCaptchaTrack.Track decodeTrack2(ImageCaptchaTrack.Track pre, byte[] bytes, int startIndex) {
        int absX = new Byte(bytes[startIndex]).intValue() - 48;
        float x = byte64to10(bytes, startIndex + 1, startIndex + 4);
        if (absX == 0) {
            x = -x;
        }

        int absY = new Byte(bytes[startIndex + 4]).intValue() - 48;
        float y = byte64to10(bytes, startIndex + 5, startIndex + 8);
        if (absY == 0) {
            y = -y;
        }

        float t = byte64to10(bytes, startIndex + 8, startIndex + 12);
        int type = new Byte(bytes[startIndex + 12]).intValue() - 48;

        String typeStr;
        switch (type) {
            case 0:
                typeStr = "down";
                break;
            case 1:
                typeStr = "move";
                break;
            case 2:
                typeStr = "up";
                break;
            case 3:
                typeStr = "click";
                break;
            default:
                typeStr = "unknown";
        }

        ImageCaptchaTrack.Track track = new ImageCaptchaTrack.Track();
        track.setX(x);
        track.setY(y);
        track.setT(t);
        track.setType(typeStr);
        if (pre != null) {
            track.setX(track.getX() + pre.getX());
            track.setY(track.getY() + pre.getY());
            track.setT(track.getT() + pre.getT());
        }
        return track;
    }

    private static ImageCaptchaTrack.Track decodeTrack(ImageCaptchaTrack.Track pre, String str) {
        int absX = Integer.parseInt(str.substring(0, 1));
        float x = string64to10(str.substring(1, 4));
        if (absX == 0) {
            x = -x;
        }

        int absY = Integer.parseInt(str.substring(4, 5));
        float y = string64to10(str.substring(5, 8));
        if (absY == 0) {
            y = -y;
        }

        float t = string64to10(str.substring(8, 12));
        int type = Integer.parseInt(str.substring(12, 13));

        String typeStr;
        switch (type) {
            case 0:
                typeStr = "down";
                break;
            case 1:
                typeStr = "move";
                break;
            case 2:
                typeStr = "up";
                break;
            case 3:
                typeStr = "click";
                break;
            default:
                typeStr = "unknown";
        }

        ImageCaptchaTrack.Track track = new ImageCaptchaTrack.Track();
        track.setX(x);
        track.setY(y);
        track.setT(t);
        track.setType(typeStr);
        if (pre != null) {
            track.setX(track.getX() + pre.getX());
            track.setY(track.getY() + pre.getY());
            track.setT(track.getT() + pre.getT());
        }
        return track;
    }


    public static long string64to10(String data) {
        int i = 0;
        int len = data.length();
        long originNumber = 0;
        while (i < len) {
            double pow = Math.pow(64, i++);
            originNumber += (long) (pow * BASE64_DICT.indexOf(data.charAt(len - i)));
        }
        return originNumber;

    }

    public static long byte64to10(byte[] bytes, int off, int end) {
        int i = 0;
//        int len = bytes.length();
        long originNumber = 0;
        while (off < end) {
            originNumber += (long) POW_64[i++] * BASE64_DICT.indexOf(bytes[end - i]);
            off++;
        }
        return originNumber;

    }

    public static void main(String[] args) {
//        System.out.println(string64to10("p0QOXyX"));
        long start = System.currentTimeMillis();
        System.out.println(decodeUnicode("123,39,97,97,39,58,49,49,44,39,98,98,39,58,50,50,125"));
        System.out.println(decrypt("p0R0A3Xp0R0Av104I02Q00T02Q01u04R03f101N10550000010001000000g110001001000V11001100000011100110000004110001000000411001100000011100010010003110011000000211001100000011100110000004110011000000611000100100031100110000001110011000000211001100000051100010010000110011000000511001100000031100110000001110011000000311001100000031100010010001110011000000211001100000021100110000003110011000000311001100000031100110000001110001001000211001100000011100110000003110011000000211001100000031100110000001110011000000311001100100021100110000001110011000000211001100000031100010010001110011000000211001100000031100110000000110011000000211001100000031100110000002110011000000211001100000011100110010003110011000000211001100000001100110000002110011000000211001100000031100110000002110011000000211000100100021100110000002110011000000211001100000011100110000003110011000000411001100000021100110000003110011001000211001100000051100110000003110011000000111001100000061100110010002110011000000511001100000021100110000005110011000000211001100000041100110000006110011000000111001100000041100010010003110011000000211001100000061100110000000110011000000511001100000041100110000004110011000000211001100000021100110000004110011000000411001100000011100110000004110001001000111001100000011100110000004110011000000011001100000031100110000004110011000000311001100000011100110000003110011000000311001100000041100110000001110011000000511001100000071100010010006110011000000411001100000081100110000008110011000000e1100010010004110011000000d110011000000i1100110000004110011000000c110011000000b110001001000511001100000021100110000002110011000000711001100000041100110000003110011000000211000100100031100110000001110011000000511001100000021100110000002110011000000411000100100001100110000003110011001000411001100000011100110000002110011000000311001100000021100110000001110001001000211001100000011100110000002110011000000311001100000011100110000002110001001000211001100000011100110000002110011000000211001100000011100110000003110001001000011001100000011100110000002110011000000111001100000021100110010002110011000000211001100000011100010010001110011000000111001100000031100110000001110011000000111001100100021100110000002110011000000211001100000011100110000003110011001000211001100000021100110000001110011000000411001100000011100010000003110001001000011001100000001100110000002110001001000211001100000011100110000003110011000000411001100000021100010000001110011000000211000100100011100110000005110011000000611001100000071100110000006110001001000211001100000061100110000046110001001000611000100100071100110000001110011000000e110001001000h1100110000003110011000001h1100010010008110011000000D110001000000b1100010010006110011000001T110001001000E110001000005y110001000002m2123,39,97,97,39,58,49,49,44,39,98,98,39,58,50,50,125"));
        long stop = System.currentTimeMillis();
        System.out.println("耗时:" + (stop - start));
    }

}
